package org.redr.canvas;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.trolltech.qt.core.QPointF;
import com.trolltech.qt.core.QRectF;
import com.trolltech.qt.core.QSizeF;
import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QDragEnterEvent;
import com.trolltech.qt.gui.QDropEvent;
import com.trolltech.qt.gui.QGraphicsItemInterface;
import com.trolltech.qt.gui.QGraphicsRectItem;
import com.trolltech.qt.gui.QGraphicsScene;
import com.trolltech.qt.gui.QGraphicsView;
import com.trolltech.qt.gui.QInputDialog;
import com.trolltech.qt.gui.QKeyEvent;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QMenu;
import com.trolltech.qt.gui.QMessageBox;
import com.trolltech.qt.gui.QMouseEvent;
import com.trolltech.qt.gui.QPainter;

public class SchemaView extends QGraphicsView{
	
	protected String 		name;
	protected boolean 		widgetDragging = false;
	protected RRWIcon 		movingWidget = null;
	protected QPointF 		mouseDownPosition = null;
	protected TempCanvasLine 	tempLine;
	protected QGraphicsRectItem 		widgetSelectionRect = null;
	protected CanvasLine	selectedLine;
	protected QMenu 		linePopup;
	protected QAction 		lineEnabledAction;
	protected QAction 		lineResetAction;
	protected QAction 		lineDeletedAction;
	protected boolean 		controlHeld = false;
	protected RRWIcon		tempWidget = null;
	
	
	public SchemaView(String n){
		super();
		QGraphicsScene scene = new QGraphicsScene();
		setScene(scene);
		name = n;
		setRenderHint(QPainter.RenderHint.Antialiasing);
		setAlignment(Qt.AlignmentFlag.AlignLeft, Qt.AlignmentFlag.AlignTop);
		linePopup = new QMenu(_("Link"), this);
		lineEnabledAction = linePopup.addAction(_("Enabled"));
		lineEnabledAction.triggered.connect(this, "toggleEnabledLink()");
		lineEnabledAction.setCheckable(true);
		linePopup.addSeparator();
		lineResetAction = linePopup.addAction(_("Reset Signals"));
		lineResetAction.triggered.connect(this, "resetLineSignals()");
		lineDeletedAction = linePopup.addAction(_("Remove"));
		lineDeletedAction.triggered.connect(this, "deleteSelectedLine()");
		linePopup.addSeparator();
		
		setAcceptDrops(true);
	}
	
	public void resetLineSignals(){
		// TODO
	}
	
	protected boolean containsRRSFile(String f){
		return f.endsWith(".rrs");
	}
	
	@Override
	protected void dragEnterEvent(QDragEnterEvent ev){
		if (containsRRSFile(ev.mimeData().data("FileName").toString())){
			ev.accept();
		} else ev.ignore();
	}
	
	@Override
	protected void dropEvent(QDropEvent ev){
		String name = ev.mimeData().data("FileName").toString();
		if (containsRRSFile(name)){
			((RedRApplication) QApplication.instance()).loadFile(name);
			ev.accept();
		} else ev.ignore();
	}
	
	protected void openActiveWidget(){
		if (tempWidget != null)
			tempWidget.instance().gui().mainWindow.show();
	}
	
	protected void renameActiveWidget(){
		List<RRWIcon> widgets = getSelectedWidgets();
        if (widgets.size() != 1) return;
        RRWIcon widget = widgets.get(0);

        String exName = widget.caption();
        //boolean ok;
        String newName = QInputDialog.getText(this, _("Rename Widget"), String.format(_("Enter new name for the '%s' widget:"), exName), QLineEdit.EchoMode.Normal, exName);
        if (newName != null & newName != exName){
            for (RRWIcon w : RedRObjects.widgetIcons()){
                if (w != widget & w.caption().equals(newName)){
                    QMessageBox.information(this, _("Red-R Canvas"), _("Unable to rename widget. An instance with that name already exists."));
                    return;
                }
            }
            widget.updateText(newName);
            widget.instance().gui().setWindowTitle(newName);
        }
    }
	
	protected void removeActiveWidget(){
		if (Boolean.parseBoolean(RedRApplication.coreSettings().getSetting("askBeforeWidgetDelete"))){
			int res = QMessageBox.question(this, _("Red-R Canvas Remove Widget"), _("Are you sure you want to remove selected widget(s)?\nThis will remove downstream data."), QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No);
			if (res != QMessageBox.StandardButton.Yes.value()) return;
		}
		List<RRWIcon> selectedWidgets = getSelectedWidgets();
		for (RRWIcon w : selectedWidgets){
			RedRObjects.removeWidget(w);
		}
		scene().update();
		tempWidget = null;
		
	}
	
	protected void toggleEnabledLink(){
		// TODO toggle the link to the enabled or disabled states
		if (selectedLine != null){
			
		}
	}
	
	protected List<RRWIcon> getSelectedWidgets(){
		List<RRWIcon> selected = new ArrayList<RRWIcon>();
		for (RRWIcon icon : RedRObjects.widgetIcons()){
			if (icon.isIconSelected()){
				selected.add(icon);
			}
		}
		return selected;
	}
	
	public void deleteSelectedLine(){
		if (selectedLine == null) return;
		deleteLine(selectedLine);
		selectedLine = null;
		scene().update();
	}
	
	public void deleteLine(CanvasLine line){
		RedRObjects.removeLineInstance(line);
	}
	
	public void selectAllWidgets(){
		for (RRWIcon icon : RedRObjects.widgetIcons()){
			if (icon.parentObject().equals(this)) icon.setSelected(true);
		}
	}
	
	public void unselectAllWidgets(){
		for (RRWIcon icon : RedRObjects.widgetIcons()){
			//if (icon.parentObject().equals(this)) 
				icon.setIconSelected(false);
		}
	}
	
	public List<QGraphicsItemInterface> getItemsAtPos(QPointF pos, Class<?> c){
		return getItemsAtPos(new QGraphicsRectItem(new QRectF(pos, new QSizeF(1,1))), c);
	}
	
	public List<QGraphicsItemInterface> getItemsAtPos(QGraphicsRectItem pos, Class<?> c){
		List<QGraphicsItemInterface> posItems = new ArrayList<QGraphicsItemInterface>();
		List<QGraphicsItemInterface> items = scene().collidingItems(pos);
		for (QGraphicsItemInterface item : items){
			if (item.getClass().equals(c)) posItems.add(item);
		}
		return posItems;
	}
	
	
	
	public String _(String s){
		return RedRApplication._(s);
	}
	
	/*
	 * Mouse Events
	 */
	
	@Override
	public void keyPressEvent(QKeyEvent ev){
		if (ev.key() == Qt.Key.Key_Control.value()) controlHeld = true;
	}
	
	public void keyReleseEvent(QKeyEvent ev){
		if (ev.key() == Qt.Key.Key_Control.value()) controlHeld = false;
	}
	
	/**
	 * Controls how mouse press events happen on the canvas.  This is an override 
	 * of the mouse press events that would normally be accepted by the canvas.
	 * Only interaction with a mouse press should call this function.
	 */
	@Override
	public void mousePressEvent(QMouseEvent ev){
		ev.accept();
		scene().update();
		mouseDownPosition = mapToScene(ev.pos());
		// System.out.println("The mouse was pressed at x: " + mouseDownPosition.x() + ", y: " + mouseDownPosition.y());
		if (widgetSelectionRect != null){
			// widgetSelectionRect.hide();
			widgetSelectionRect = null;
		}
		
		if (!(ev.modifiers().isSet(Qt.KeyboardModifier.ControlModifier))) unselectAllWidgets();
		if (ev.button() == Qt.MouseButton.LeftButton){
			List<RRWIcon> icons = new ArrayList<RRWIcon>();
			for (RRWIcon icon : RedRObjects.getIconsByTab(name)){
				if (icon.insideRightChannel(mouseDownPosition)) icons.add(icon);
				else if (icon.insideLeftChannel(mouseDownPosition)) icons.add(icon);
			}
			if (icons.size() > 0){
				tempWidget = icons.get(0);
				unselectAllWidgets();
				
				// tempLine is a canvas line that is drawn to connect two
				// widgets on the canvas.
				
				tempLine = new TempCanvasLine(scene());
				
				if (tempWidget.getDistToLeftEdgePoint(mouseDownPosition) < tempWidget.getDistToRightEdgePoint(mouseDownPosition)){
					System.out.println("Connecting to input channel.");
					tempLine.setEndWidget(tempWidget);
					for (RRWIcon widget : RedRObjects.widgetIcons()){
						widget.canConnect(widget, tempWidget);
					}
				} else {
					System.out.println("Connecting to output channel.");
					tempLine.setStartWidget(tempWidget);
					for (RRWIcon widget : RedRObjects.widgetIcons()){
						widget.canConnect(tempWidget, widget);
					}
				}
				
				scene().update();
				return;
			}
		}
		
		QGraphicsItemInterface activeItem = scene().itemAt(mouseDownPosition);
		if (activeItem == null){
			tempWidget = null;
			widgetSelectionRect = new QGraphicsRectItem(new QRectF(mouseDownPosition, mouseDownPosition), null, scene());
			widgetSelectionRect.setZValue(-100);
			widgetSelectionRect.show();
			unselectAllWidgets();
			for (RRWIcon i : RedRObjects.widgetIcons()){
				i.setPossibleConnection(false);
			}
		} else {
			if (activeItem instanceof RRWIcon){
				System.out.println("Clicked on an icon");
				tempWidget = (RRWIcon) activeItem;
				if (ev.button().equals(Qt.MouseButton.LeftButton)){
					System.out.println("Setting icon selected: " + tempWidget.instance().caption());
					widgetDragging = true;
					if (!tempWidget.isSelected()){
						System.out.println("Setting icon selected.");
						unselectAllWidgets();
						tempWidget.setIconSelected(true);
						System.out.println("tempwidget selection was set to:" + tempWidget.isSelected());
					}
					for (RRWIcon i : getSelectedWidgets()){
						i.savePosition();
						i.setAllLinesFinished(false);
					}
				} else if (ev.button().equals(Qt.MouseButton.RightButton)){
					System.out.println("Setting widget popup");
					unselectAllWidgets();
					tempWidget.setIconSelected(true);
					// TODO turn on the widget popup to control the icon.
				} else System.out.println("Button pressed value = " + ev.button().value());
			} else if ((activeItem instanceof CanvasLine) && (ev.button().equals(Qt.MouseButton.RightButton))){
				unselectAllWidgets();
				selectedLine = (CanvasLine) activeItem;
				lineEnabledAction.setChecked(selectedLine.getEnabled());
				// TODO turn on the line popup to control the line.
			} else unselectAllWidgets();
		}
		
		// TODO set the widget popup to be disabled once we have the widget popup activities
		scene().update();
	}
	
	@Override
	public void mouseMoveEvent(QMouseEvent ev){
		if (mouseDownPosition == null){
			System.out.println("Mouse down position is null, returning with no update.");
			ev.ignore();
			return;
		}
		QPointF point = mapToScene(ev.pos());
		if (widgetDragging && (mouseDownPosition != null)){
			for (RRWIcon i : getSelectedWidgets()){
//				System.out.println("Move point:" + point.x() + point.y());
				// System.out.println("Original point:" + mouseDownPosition.x() + mouseDownPosition.y());
				
				QPointF diff = new QPointF(point.x() - mouseDownPosition.x(), point.y() - mouseDownPosition.y());
//				System.out.println("Diff:" + diff.x() + diff.y());
				QPointF newPos = new QPointF(i.getOldPos().x() + diff.x(), i.getOldPos().y() + diff.y());
//				System.out.println("Old Position:" + i.getOldPos().x() + i.getOldPos().y());
//				System.out.println("New Position:" + newPos.x() + newPos.y());
				i.setCoords(newPos.x(), newPos.y());
				ensureVisible(i);
			}
		} else if (tempLine != null){
			// System.out.println("Updating temp line.");
			tempLine.updateLinePos(point);
		} else if ((widgetSelectionRect != null) && (mouseDownPosition != null)){
			widgetSelectionRect.setRect(new QRectF(mouseDownPosition, point));
		}
		
		scene().update();
	}
	
	@Override
	public void mouseReleaseEvent(QMouseEvent ev){
		QPointF point = mapToScene(ev.pos());
		if (widgetSelectionRect != null){
			List<QGraphicsItemInterface> widgets = getItemsAtPos(widgetSelectionRect, RRWIcon.class);
			for (RRWIcon i : RedRObjects.widgetIcons()){
				i.setSelected(widgets.contains(i));
			}
			widgetSelectionRect.hide();
			widgetSelectionRect = null;
		}
		
		if (widgetDragging){
			boolean validPos = true;
			List<RRWIcon> selectedIcons = getSelectedWidgets();
			for (RRWIcon i : selectedIcons){
				if (!validPos) break;
				List<QGraphicsItemInterface> collisions = scene().collidingItems(i);
				for (QGraphicsItemInterface gi : collisions){
					if (gi instanceof RRWIcon) validPos = false; break;
				}
			}
			
			for (RRWIcon i : selectedIcons){
				if (!validPos) i.restorePosition();
				i.updateTooltip();
				i.setAllLinesFinished(true);
			}
			
			
		} else if (tempLine != null){
			System.out.println("Templine released");
			
			RRWIcon start = tempLine.startWidget() == null ? 
					tempLine.widget() : tempLine.startWidget();
			RRWIcon end = tempLine.endWidget() == null ? 
					tempLine.widget() : tempLine.endWidget();
//			if (tempLine.startWidget() == null)
//				System.out.println("Startwidget is null");
//			if (tempLine.endWidget() == null)
//				System.out.println("EndWidget is null");
//			if (tempLine.widget())
//			System.out.println(start.instance().widgetID());
//			System.out.println(end.instance().widgetID());
			
			
					
			if ((start != null) && (end != null)){
				if ((!start.equals(end))) {
					RedRObjects.addLine(start, end);
					System.out.println(String.format("Trying to create connection between %s and %s", start.instance().caption(), end.instance().caption()));
				}
			} else {
				QPointF newCoords = new QPointF(ev.globalPos());
				// TODO add code for the widget popup here.
				/*
				 *  import redRQTCore, redRCanvasToolbar
                dialog = redRQTCore.dialog(None, title = "Widget Lookup")
                def searchCallback(info, c, x = point.x(), y = point.y(), start = start, end = end, self = self, dialog = dialog):
                    import redRObjects
                    if c == 'w':
                        newWidget = redRObjects.addWidget(redRObjects.widgetRegistry()['widgets'][info.fileName], x = x, y = y) # add the correct widget to the schema
                        nw = redRObjects.getWidgetByIDActiveTabOnly(newWidget)
                        self.doc.addLine(start or nw, end or nw)
                    dialog.accept()
                    return
                searchBox = redRCanvasToolbar.SearchBox2(dialog, width = 500, callback = searchCallback)
                searchBox.setItems(redRObjects.widgetRegistry()['widgets'], [])
                dialog.exec_()
                del searchBox
                del dialog
				 */
				
				// close the templine
				
			}
			tempLine.hide();
			tempLine = null;
		} else if (ev.button().equals(Qt.MouseButton.RightButton)){
//			QGraphicsItemInterface activeItem = scene().itemAt(point);
//			QPointF diff = mouseDownPosition.subtract(point);
//			if not activeItem and (diff.x()**2 + diff.y()**2) < 25:     # if no active widgets and we pressed and released mouse at approx same position
//            import redRQTCore, redRCanvasToolbar
//            dialog = redRQTCore.dialog(None, title = "Widget Lookup")
//            def searchCallback(info, c, x = point.x(), y = point.y(), self = self, dialog = dialog):
//                import redRObjects
//                if c == 'w':
//                    newWidget = redRObjects.addWidget(redRObjects.widgetRegistry()['widgets'][info.fileName], x = x, y = y) # add the correct widget to the schema
//                    #nw = redRObjects.getWidgetByIDActiveTabOnly(newWidget)
//                    #self.doc.addLine(start or nw, end or nw)
//                dialog.accept()
//                return
//            searchBox = redRCanvasToolbar.SearchBox2(dialog, width = 500, callback = searchCallback)
//            searchBox.setItems(redRObjects.widgetRegistry()['widgets'], [])
//            dialog.exec_()
			
		}
		
		scene().update();
		widgetDragging = false;
		// self.doc.canvasDlg.widgetPopup.setEnabled(len(self.getSelectedWidgets()) == 1)
		
	}
	
	@Override
	public void mouseDoubleClickEvent(QMouseEvent ev){
		QPointF point = mapToScene(ev.pos());
		QGraphicsItemInterface activeItem = scene().itemAt(point);
		if (activeItem instanceof RRWIcon){
			tempWidget = (RRWIcon) activeItem;
			openActiveWidget();
		} else if (activeItem instanceof CanvasLine){
			System.out.println("Opening canvas dialog for active line.");
			// open signal manager dialog
		} else {
			System.out.println("Active item was of type " + activeItem.toString());
		}
	}

	public RRWIcon getIconAtPos(QPointF point) {
		// TODO Auto-generated method stub
		Collection<RRWIcon> icons = RedRObjects.getIconsByTab(this.name);
		for (RRWIcon i : icons){
			if (i.nearPoint(point))	{ return i; }
		}
		return null;
	}
	
	

}
